FlutterでローカルにKey-Valueでデータ管理するshared_preferencesを試してみた
こんにちは、ゲームソリューション部のsoraです。
今回は、FlutterでローカルにKey-Valueでデータ管理するshared_preferencesを試してみたことについて書いていきます。
実装した画面
・ローカルに保存しているデータを取得して、現在の名前として表示する。(初期値は「あなた」に設定)
・入力欄に名前を入力して、ボタンを押下すると、ローカルに保存しているデータを書き換えて表示する。
・アプリを再起動してみても、ローカルに保存しているデータを取得して表示しているため、変更後の名前で表示される。
利用する主要なパッケージ
shared_preferences
shared_preferences | Flutter package
使用したバージョン:shared_preferences: ^2.2.2
Key-Value形式でローカルにデータを保存するパッケージ
Providerなどの状態管理はアプリを落とすとリセットされるが、ローカルにデータを保存することで再起動時もデータが保持される。
暗号化されずに保存されるため、暗号化が必要なデータはflutter_secure_storage
を使用するか、サーバ側で保存してください。
shared_preferences
はアプリを削除するとデータも削除されますが、flutter_secure_storage
はiOSとAndroidで挙動が異なるらしいです。
Androidはアプリは削除するとデータも削除されますが、iOSはアプリを削除してもデータは残るらしいです。
flutter_riverpod
flutter_riverpod | Flutter package
使用したバージョン:flutter_riverpod: ^2.5.1
状態管理パッケージ
多くの情報が落ちている人気なパッケージのため、詳しい説明は割愛します。
コードの解説
コードは以下です。
今回はテストのため、ファイル分けせずに全てmain.dart
に書いています。
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; // shared_preferencesインスタンス final sharedPreferencesProvider = Provider<SharedPreferences>( (_) => throw UnimplementedError() ); // NotifierProviderでデータの取得・登録 class SharedPrefTest extends Notifier<String> { @override String build(){ // Notifierクラスのbuild内でrefを使用可能 final prefs = ref.watch(sharedPreferencesProvider); String prefName = prefs.getString('my_name') ?? 'あなた'; return prefName; } void setName(WidgetRef ref, String name) { final prefs = ref.watch(sharedPreferencesProvider); prefs.setString('my_name', name); } void getName(WidgetRef ref) { final prefs = ref.watch(sharedPreferencesProvider); // my_nameキーの値を取得(空だった場合は空文字を返す) String prefName = prefs.getString('my_name') ?? ''; state = prefName; } } final sharedPrefTestProvider = NotifierProvider<SharedPrefTest, String>(() { return SharedPrefTest(); }); // main Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( ProviderScope( // 初期起動時にSharedPreferencesでインスタンスの取得 overrides:[ sharedPreferencesProvider.overrideWithValue( await SharedPreferences.getInstance() ), ], child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(), home: MyHomePage(), ); } } class MyHomePage extends ConsumerWidget { MyHomePage({super.key}); final _messageController = TextEditingController(); @override Widget build(BuildContext context, WidgetRef ref) { final providerValue = ref.watch(sharedPrefTestProvider); final providerNotifier = ref.watch(sharedPrefTestProvider.notifier); return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('shared_preferences test'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: Text( '現在の名前:${providerValue}', style: const TextStyle( fontSize: 18, ), ), ), const SizedBox(height: 10), Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: TextField( controller: _messageController, maxLines: 1, decoration: const InputDecoration( hintText: '名前を入力', hintStyle: TextStyle(color: Colors.black54), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( child: const Text('名前変更'), onPressed: (){ var msg = _messageController.text.trim(); if(msg.isEmpty){ return; } // shared_preferences周りの処理 // 入力した名前の登録 providerNotifier.setName(ref, msg); // 入力した名前で状態を更新 providerNotifier.getName(ref); _messageController.clear(); }, ), ] ), ], ), ), ); } }
SharedPreferencesのインスタンス取得
まずSharedPreferencesのインスタンスを取得する必要があって、本来であれば以下のように記述します。
// SharedPreferencesインスタンスの取得 final pref = await SharedPreferences.getInstance(); // 値の登録 pref.setString('key', value);
このインスタンス取得の際に、awaitが入っていて非同期処理で記述しなければならず、毎回記載するのは面倒で使いづらいため、最初に1度だけインスタンスを取得するように記述しています。
final sharedPreferencesProvider = Provider<SharedPreferences>( (_) => throw UnimplementedError() ); // ... // main Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( ProviderScope( // 初期起動時にSharedPreferencesのインスタンスを取得 overrides:[ sharedPreferencesProvider.overrideWithValue( await SharedPreferences.getInstance() ), ], child: const MyApp(), ), ); }
shared_preferencesでのデータの設定・取得
shared_preferencesでローカルのデータを取得したり、設定したりします。
Notifierクラス内のメソッドとして、データの取得・登録を定義しています。
今回はStirngのため、setString, getString
です。
intであればsetInt, getInt
など、型に応じて変わります。
// NotifierProviderでデータの取得・登録 class SharedPrefTest extends Notifier<String> { @override String build(){ // Notifierクラスのbuild内でrefを使用可能 final prefs = ref.watch(sharedPreferencesProvider); String prefName = prefs.getString('my_name') ?? 'あなた'; return prefName; } void setName(WidgetRef ref, String name) { final prefs = ref.watch(sharedPreferencesProvider); // my_nameキーに値を設定 prefs.setString('my_name', name); } void getName(WidgetRef ref) { final prefs = ref.watch(sharedPreferencesProvider); // my_nameキーの値を取得(空だった場合は空文字を返す) String prefName = prefs.getString('my_name') ?? ''; state = prefName; } } final sharedPrefTestProvider = NotifierProvider<SharedPrefTest, String>(() { return SharedPrefTest(); }); // ... ElevatedButton( child: const Text('名前変更'), onPressed: (){ var msg = _messageController.text.trim(); if(msg.isEmpty){ return; } // shared_preferences周りの処理 // 入力した名前の登録 providerNotifier.setName(ref, msg); // 入力した名前で状態を更新 providerNotifier.getName(ref); _messageController.clear(); }, ),
最後に
今回は、FlutterでローカルにKey-Valueでデータ管理するshared_preferencesを試してみたことを記事にしました。
どなたかの参考になると幸いです。